// 用户可以编辑文件  $HOME/.rand-reader.conf 改变发音命令，
// 默认值是:
// xfttsgo
// -stdin
// 第一行是命令，后面的各行是参数，内容从标准输入（STDIN）发送给命令
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"time"

	"fyne.io/fyne/v2/dialog"

	"os/exec"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/widget"
)

var commandv []string

func main() {
	var name = flag.String("f", "", "指定配置文件，默认使用：HOME/.rand-reader.conf")
	flag.Parse()
	if *name != "" {
		getCommandv(*name)
	}
	createGui()
}

func init() {
	home, err := os.UserHomeDir()
	if err != nil {
		return
	}
	name := filepath.Join(home, ".rand-reader.conf")
	getCommandv(name)
}

func getCommandv(name string) {
	v, err := getList(name)
	if err == nil && len(v) > 0 {
		commandv = v
		fmt.Println("语音合成程序：", commandv[0])
	} else {
		commandv = nil
	}
}

func getList(name string) ([]string, error) {
	fp, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	defer fp.Close()
	res := []string{}
	rd1 := bufio.NewReader(fp)
	for {
		line1, err := rd1.ReadString('\n')
		line1 = strings.TrimSpace(line1)
		if err != nil || len(line1) == 0 {
			break
		}
		res = append(res, line1)
	}
	return res, nil
}

func reduce(v []int, n int) []int {
	return append(v[0:n], v[n+1:len(v)]...)
}

func getRandIdx(n int) []int {
	var idxs = make([]int, n)
	var ret = make([]int, n)
	r := rand.New(rand.NewSource(time.Now().UnixMilli()))
	for i := 0; i < n; i++ {
		idxs[i] = i
	}
	for i := 0; i < n; i++ {
		rn := r.Intn(n - i)
		for {
			if i == 0 && rn == 0 {
				rn = r.Intn(n - i)
			} else {
				break
			}
		}
		ret[i] = idxs[rn]
		idxs = reduce(idxs, rn)
	}
	return ret
}

func getAscIndex(n int) []int {
	var idxs = make([]int, n)
	for i := 0; i < n; i++ {
		idxs[i] = i
	}
	return idxs
}

func playString(s string) error {
	command1 := []string{"xfttsgo", "-stdin"}
	if commandv != nil {
		command1 = commandv
	}
	exe1 := exec.Command(command1[0], command1[1:]...)
	input1, err := exe1.StdinPipe()
	if err != nil {
		//fmt.Println(err.Error())
		return err
	}
	err = exe1.Start()
	if err != nil {
		return err
	}
	_, err = input1.Write([]byte(s + "\n"))
	if err != nil {
		return err
	}
	err = input1.Close()
	if err != nil {
		return err
	}
	err = exe1.Wait()
	if err != nil {
		return err
	}
	return err
}

func createGui() {
	var lines []string
	var rindexs []int
	var idx = -1
	var labelBottom *widget.Label
	var labelFile *widget.Label
	var labelNum *widget.Label
	var labelMode *widget.Label
	var selector *widget.Select
	var errLabel *widget.Label
	app := app.New()
	app.Settings().SetTheme(GetMyTheme())
	w := app.NewWindow("rand-reader")

	btOpen := widget.NewButton("打开", func() {
		dialog.ShowFileOpen(func(uri1 fyne.URIReadCloser, err error) {
			if err == nil {
				path1 := uri1.URI().Path()
				fmt.Println(path1)
				lines, err = getList(path1)
				selector.Options = lines
				if err != nil {
					lines = nil
					labelBottom.SetText(err.Error())
				} else {
					name1 := filepath.Base(path1)
					labelFile.SetText(name1)
					labelNum.SetText(fmt.Sprintf("总数：%v", len(lines)))
					rindexs = getRandIdx(len(lines))
					idx = -1
					labelMode.SetText("工作模式：乱序")
				}

			}
		}, w)
	})

	labelFile = widget.NewLabel("")

	labelNum = widget.NewLabel("0")

	box1 := container.NewHBox(btOpen, labelFile, labelNum)

	btLast := widget.NewButton("上一个", func() {
		if lines == nil {
			err := playString("请先打开文件输入内容")
			if err != nil {
				errLabel.SetText(err.Error())
			}
			return
		}
		if idx <= 0 {
			fmt.Println("已到头部")
			idx = 0
		} else {
			idx -= 1
		}

		labelBottom.SetText(fmt.Sprintf("目前序号：%v", idx+1))
		err := playString(lines[rindexs[idx]])
		if err != nil {
			errLabel.SetText(err.Error())
		}
	})

	btAgain := widget.NewButton("再读一次", func() {
		if lines == nil {
			err := playString("请先打开文件输入内容")
			if err != nil {
				errLabel.SetText(err.Error())
			}
			return
		}
		if idx < 0 {
			idx = 0
		}

		labelBottom.SetText(fmt.Sprintf("目前序号：%v", idx+1))
		err := playString(lines[rindexs[idx]])
		if err != nil {
			errLabel.SetText(err.Error())
		}
	})

	btNext := widget.NewButton("下一个", func() {
		if lines == nil {
			err := playString("请先打开文件输入内容")
			if err != nil {
				errLabel.SetText(err.Error())
			}
			return
		}
		if idx < len(lines)-1 {
			idx += 1
		} else {
			fmt.Println("已到底部")
		}

		labelBottom.SetText(fmt.Sprintf("目前序号：%v", idx+1))
		err := playString(lines[rindexs[idx]])
		if err != nil {
			errLabel.SetText(err.Error())
		}
	})
	btToHead := widget.NewButton("从头开始", func() {
		if lines == nil {
			err := playString("请先打开文件输入内容")
			if err != nil {
				errLabel.SetText(err.Error())
			}
			return
		}
		idx = -1
		labelBottom.SetText("")
	})
	btReset := widget.NewButton("重新排序", func() {
		if lines == nil {
			err := playString("请先打开文件输入内容")
			if err != nil {
				errLabel.SetText(err.Error())
			}
			return
		}
		rindexs = getRandIdx(len(lines))
		idx = -1
		labelMode.SetText("工作模式：乱序")
		labelBottom.SetText("")
	})
	btAscOrder := widget.NewButton("顺序阅读", func() {
		if lines == nil {
			err := playString("请先打开文件输入内容")
			if err != nil {
				errLabel.SetText(err.Error())
			}
			return
		}
		rindexs = getAscIndex(len(lines))
		idx = -1
		labelMode.SetText("工作模式：顺序")
		labelBottom.SetText("")
	})

	box2 := container.NewHBox(btLast, btAgain, btNext, btToHead, btReset, btAscOrder)

	labelMode = widget.NewLabel("")

	labelBottom = widget.NewLabel("")

	labelRead := widget.NewLabel("朗读内容：")
	selector = widget.NewSelect(lines, func(s string) {
		err := playString(s)
		if err != nil {
			errLabel.SetText(err.Error())
		}
	})
	boxSelect := container.NewHBox(labelRead, selector)

	inputRead := widget.NewEntry()
	inputRead.SetPlaceHolder("输入内容后，点击‘朗读’听读音")
	inputRead.SetMinRowsVisible(2)
	btRead := widget.NewButton("朗读", func() {
		txt := strings.TrimSpace(inputRead.Text)
		txt = strings.Trim(txt, ".;,!?。！？：；，")
		if len(txt) == 0 {
			return
		}
		err := playString(txt)
		if err != nil {
			errLabel.SetText(err.Error())
		}
	})
	//boxRead := container.NewHBox(labelRead, inputRead)
	aboutTxt := `配置文件说明：
	文件路径是：$HOME/.rand-reader.conf
	第一行是TTS命令，后面几行是参数。
	TTS程序的要求是：从标准输入读入文字。
`
	aboutTxt = aboutTxt + getConfig()
	labelAbout := widget.NewLabel(aboutTxt)

	errLabel = widget.NewLabel("")

	winBox := container.NewVBox(box1, box2, labelMode, labelBottom, boxSelect, inputRead, btRead, labelAbout, errLabel)

	w.SetContent(winBox)

	w.SetOnClosed(func() {
		app.Quit()
	})

	w.Resize(fyne.Size{Width: 800, Height: 600})
	w.SetFixedSize(true)

	w.ShowAndRun()
}

func getConfig() string {
	home1, err := os.UserHomeDir()
	if err != nil {
		return ""
	}
	conf1 := filepath.Join(home1, ".rand-reader.conf")
	data1, err := ioutil.ReadFile(conf1)
	if err != nil {
		return ""
	}
	return fmt.Sprintf("\n当前TTS配置：\n%v", string(data1))
}
